﻿using System;
using System.IO;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Browser;
using System.Reflection;
using SilverQuery;

namespace SilverPortal
{
	public abstract class SilverPortalComponent : SilverQueryComponent
	{
		private Dictionary<string, SilverPortalModule> _moduleRegistry = 
			new Dictionary<string, SilverPortalModule>();

		protected SilverPortalComponent()
		{
			this.Log = new Logger();
		}

		protected PortalState State
		{
			get;
			private set;
		}

		protected Logger Log
		{
			get;
			private set;
		}

		protected void Load(Uri xmlConfigUrl, Callback callback)
		{
			Ajax.get(xmlConfigUrl.ToString(), null, (data, error) =>
				{
					if (error != null)
						DisplayError("Could not load config from " + xmlConfigUrl.ToString(), error);
					else
					{
						Load(XDocument.Parse(data));
						LoadTabContent(CurrentTabNumber);
						callback();
					}
				});
		}

		private void Load(XDocument xmlConfig)
		{
			// Add the silverlight css to the page.
			AppendMainCss();

			this.Config = new PortalConfig(xmlConfig);
			//TODO: Allow a custom state provider type to be specified in config.
			this.State = new PortalState(new CookieStateProvider());
			BuildDebugConsole();
			InitializeTabs();
		}

		private void BuildDebugConsole()
		{
			var console = @q(@"<div class=""module"" id=""debugConsole"">" +
				@"<div class=""header""><span>Debug Console</span><div class=""right"">" +
				@"<a id=""clearDebugConsole"">clear</a><a id=""closeDebugConsole"">close</a></div></div>" +
				@"<div class=""content""></div></div>");

			@q(HtmlPage.Document.Body).append(console);
			@q("#clearDebugConsole").click(e => { 
				@q("div.content", console).empty(); return false; 
			});
			@q("#closeDebugConsole").click(e => { console.hide(); return false; });
		}

		private void InitializeTabs()
		{
			int selectedTab = this.CurrentTabNumber;
			for (int i = 0; i < this.Config.Tabs.Count; i++)
			{
				var tab = this.Config.Tabs[i];
				@q("<li/>").attr("id", "tab" + (i + 1))
					.addClass(i == (selectedTab - 1) ? "selected" : string.Empty)
					.html(tab.Title)
					.mouseover(e =>
					{
						var target = @q(e.target);
						if (!target.hasClass("selected"))
							target.addClass("hover");
						return false;
					})
					.mouseout(e =>
					{
						@q(e.target).removeClass("hover");
						return false;
					})
					.click(OnTabClick)
					.appendTo("ul#tabs");

				for (int j = 0; j < tab.Modules.Count; j++)
				{
					CreateModuleContainer(tab.Modules[j], j, i + 1, selectedTab);
				}
			}
		}

		private bool OnTabClick(Event e)
		{
			// Don't do anything if the clicked tab is already selected.
			if (@q(e.target).hasClass("selected"))
				return false;

			// Hide the currently selected tab.
			string selectedTab = @q("ul#tabs>li.selected").removeClass("selected").attr("id");
			if (!String.IsNullOrEmpty(selectedTab))
				@q("#content div." + selectedTab).hide();

			// Make the clicked tab selected and show its content.
			string clickedTab = @q(e.target).attr("id");
			@q("#content div." + clickedTab).show();
			@q(e.target).removeClass("hover").addClass("selected");

			int tabNumber = Int32.Parse(clickedTab.Substring(3));

			// Store the selected tab number in a cookie.
			CurrentTabNumber = tabNumber;
			LoadTabContent(tabNumber);

			return false;
		}
		
		private void CreateModuleContainer(ModuleConfig moduleConfig, int moduleIndex, int tabNumber, int selectedTabNumber)
		{
			// Create a uniqueId for this module instance.
			string id = String.Format("{0}-{1}-{2}",
				moduleConfig.TypeName.ToLower(), moduleIndex, tabNumber);

			// Create the moduleConfig container.
			var container = @q("<div/>").css("display", (tabNumber == selectedTabNumber) ? "block" : "none")
				.addClass(moduleConfig.Css + " tab" + tabNumber)
				.appendTo("#content");

			var moduleElement = @q(ModuleTemplateHtml);
			@q("div.header>span", moduleElement).html(moduleConfig.Title);
			moduleElement.attr("id", id).appendTo(container);
		}

		private void AppendMainCss()
		{
			var assembly = Assembly.GetExecutingAssembly();
			string css;
			using (var sr = new StreamReader(assembly.GetManifestResourceStream("SilverPortal.silverportal.css")))
			{
				css = sr.ReadToEnd();
			}
			@q("style[title='silverPortal']").html(css);
		}

		private void LoadTabContent(int tabNumber)
		{
			var tabConfig = this.Config.Tabs[tabNumber - 1];

			foreach (HtmlElement moduleElement in @q("div.tab" + tabNumber + " div.module"))
			{
				var id = @q(moduleElement).attr("id");
				SilverPortalModule module;
				if (!_moduleRegistry.TryGetValue(id, out module))
				{
					int moduleIndex = Int32.Parse(id.Split('-')[1]);
					var moduleConfig = tabConfig.Modules[moduleIndex];
					var contentElement = @q("div.content", moduleElement);

					ModuleFactory.CreateAsync(moduleConfig, (m, error) =>
					{
						module = m;
						if (error != null)
						{
							SilverPortalModule.DisplayError(
								"Could not create module " + moduleConfig.TypeName,
								error, contentElement);
						}
						else
						{
							module.Container = contentElement;
							module.Log = this.Log;
							module.State = this.State;
							module.LoadInternal();
							_moduleRegistry[id] = module;
						}
					});
				}
				else
				{
					if (!module.Loaded)
						module.LoadInternal();
				}
			}
		}


		private void DisplayError(string message, Exception e)
		{

		}

		protected int CurrentTabNumber
		{
			get
			{
				int tab;
				if (Int32.TryParse(this.State["currentTab"], out tab))
					return tab;
				return 1;
			}
			set
			{
				State["currentTab"] = value.ToString();
			}
		}

		public static string CurrentUriPath()
		{
			string uri = HtmlPage.Document.DocumentUri.AbsoluteUri;
			return uri.Substring(0, uri.LastIndexOf('/') + 1);
		}

		protected PortalConfig Config
		{
			get;
			private set;
		}

		private const string ModuleTemplateHtml = @"<div class=""module fill"">
		<div class=""header""><span></span><div class=""right""></div></div>
		<div class=""content"">
			<div class=""loading"" />
		</div>
	</div>";
	}
}
